import socket
import threading
import xmlrpclib
import Queue
import SimpleXMLRPCServer

from pprint import pprint

from sorto_board  import SortoBoard

class RemotePlayerInfo(object):
    def __init__(self, name, ip, port):
        self.name = name
        self.ip   = ip
        self.port = port
        return
    pass # End of class RemotePlayerInfo
        

class SortoXMLRPCServerThread(threading.Thread):

    class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
        rpc_paths = ('/sorto',)
        pass # End of class RequestHandler

    def __init__(self, parent, server_ip='127.0.0.1', port='8000'):
        threading.Thread.__init__(self)
        self._parent     = parent
        self._ip_address = server_ip
        self._port       = int(port)
        self._abort      = False
        self.XMLRPC_SERVER = None
        self._players    = {}

        # This queue is read by the controller before each game to
        # load any new remote players.
        #self.new_player_queue = Queue.Queue()
        #self.remove_player_queue = Queue.Queue()

        while self.XMLRPC_SERVER is None:
            try:
                handlr = SortoXMLRPCServerThread.RequestHandler

                self.XMLRPC_SERVER = \
                    SimpleXMLRPCServer.SimpleXMLRPCServer((self._ip_address,
                                                           self._port),
                                                          requestHandler=handlr,
                                                          logRequests=False,
                                                          allow_none=True,
                                                          bind_and_activate=False)
                SimpleXMLRPCServer.allow_reuse_address = True
                self.XMLRPC_SERVER.server_bind()
                self.XMLRPC_SERVER.server_activate()
                self.XMLRPC_SERVER.register_introspection_functions()
                self.XMLRPC_SERVER.register_function(self.register_player, 
                                                     'register_player')
                self.XMLRPC_SERVER.register_function(self.unregister_player, 
                                                     'unregister_player')
            except socket.error, ex:
                raise ex
            pass
        return

    def abort(self):
        self._abort = True
        return

    def register_player(self, player_name, player_ip, player_port):
        while player_name in self._players:
            player_name += ".1"
            pass
        
        player_name = self._parent.register_remote_player(player_name, player_ip, player_port)

        return player_name

    def unregister_player(self, player_name):
        self._parent.unregister_remote_player(player_name)
        return

    def run(self):
        self.XMLRPC_SERVER.timeout = 0.1
        while not self._abort:
            if self.XMLRPC_SERVER:
                self.XMLRPC_SERVER.handle_request()
                pass
            pass
        try:
            self.XMLRPC_SERVER.server_close()
        except socket.error, ex:
            if ex[0] == 61:
                pass
            else:
                raise ex
            pass
        self.XMLRPC_SERVER = None
        return
    
    pass # End of class SortoXMLRPCServerThread


class SortoXMLRPCPlayerThread(threading.Thread):

    class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
        rpc_paths = ('/sorto_player',)
        pass # End of class RequestHandler

    def __init__(self, view, player_module, server_ip, server_port):
        threading.Thread.__init__(self)
        self._view          = view
        self._player_module = player_module
        self._server_ip     = server_ip
        self._server_port   = server_port
        self._player        = player_module.SortoPlayer(self._view)
        self._player_name   = self._player.get_name()
        self._point_total   = 0
        self._port          = 8001
        self._abort         = False
        self._game_number   = None
        self._remote_server = None
        self._player_colors = None

        self._ip_address = self._determine_ip_address_to_host(self._server_ip)

        self._register_with_server(self._server_ip, self._server_port)
        
        return

    def _determine_ip_address_to_host(self, server_address):
        import socket

        # Strip off the http:// and port number :8000
        index = server_address.find(':')
        if index > 0:
            server_address = server_address[index+3:]
            pass

        index = server_address.rfind(':')
        if index > 0:
            server_address = server_address[:index]
            pass

        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            s.connect((server_address, 9))
            client = s.getsockname()[0]
        except socket.error:
            client = None
        finally:
            del s
            pass
        return client

    def _register_with_server(self, server, port):
        if not server.lower().startswith('http://'):
            server = 'http://' + server + ':' + str(port) + '/sorto'
            pass

        s = xmlrpclib.ServerProxy(server)
        remote_methods = s.system.listMethods()
        self._remote_server = s

        if 'register_player' not in remote_methods:
            raise socket.error()

        # Startup our own SimpleXMLRPC server so the controller can
        # call our play_game_turn
        while 1:
            try:
                handlr = SortoXMLRPCPlayerThread.RequestHandler
                self._xmlrpc_server = \
                    SimpleXMLRPCServer.SimpleXMLRPCServer((self._ip_address,
                                                           self._port),
                                                          requestHandler=handlr,
                                                          logRequests=False,
                                                          allow_none=True,
                                                          bind_and_activate=False)
                self._xmlrpc_server.allow_reuse_address = True
                self._xmlrpc_server.server_bind()
                self._xmlrpc_server.server_activate()
                
                self._xmlrpc_server.register_function(self.get_name, 
                                                      'get_name')
                self._xmlrpc_server.register_function(self.get_color, 
                                                      'get_color')
                self._xmlrpc_server.register_function(self.set_name, 
                                                      'set_name')
                self._xmlrpc_server.register_function(self.get_seed, 
                                                      'get_seed')
                self._xmlrpc_server.register_function(self.play_game_turn, 
                                                      'play_game_turn')
                self._xmlrpc_server.register_function(self.get_turn_results, 
                                                      'get_turn_results')
                self._xmlrpc_server.register_function(self.simulation_over, 
                                                      'simulation_over')
                self._xmlrpc_server.register_function(self.abort, 
                                                      'abort')
                self._player_name = self._remote_server.register_player(self._player_name, 
                                                                        self._ip_address, 
                                                                        self._port)
                self._player.set_name(self._player_name)
            except socket.error, ex:
                if ex[0] == 48:
                    self._port += 1
                    continue
                pass
            break
        return True

    def get_name(self):
        res = self._player_name
        return res

    def set_name(self, name):
        self._player_name = name
        return self._player.set_name(name)

    def get_color(self):
        res = self._player.get_color()
        return res

    def get_seed(self):
        res = self._player.get_seed()
        return res

    def play_game_turn(self, game_number, round, roll, 
                       playing_counters, previous_board,
                       previous_counter, player_colors,
                       last_games_seed):
        # Convert the board types back into board types...
        boards   = {}
        counters = {}
        self._player_colors = player_colors
        for name in previous_board:
            boards[name] = SortoBoard(previous_board[name]['_board'])
            counters[name] = previous_counter[name]
            pass

        if self._view:
            if game_number != self._game_number:
                self._game_number = game_number
                self._view.set_game_number(game_number)
                pass
            self._view.set_round(round)
            self._view.set_die_roll(roll)
            for name in boards:
                board = boards[name]
                self._view.set_board_state(name,
                                           player_colors,
                                           board, 
                                           counters[name])
                point_total = board.longest_chain()
                if name != self._player_name:
                    self._view.set_points_won(name, point_total)    
                    pass
                pass
            pass
        self._player.play_game_turn(game_number, 
                                    round, 
                                    roll, 
                                    playing_counters,
                                    boards,
                                    previous_counter,
                                    player_colors,
                                    last_games_seed)
        return
    
    def get_turn_results(self):
        res = self._player.get_turn_results()
        (board, counters) = res
        if self._view:
            self._view.set_board_state(self._player_name,
                                       self._player_colors,
                                       board, counters)
            self._point_total = board.longest_chain()
            self._view.set_points_won(self._player_name, self._point_total)    
            pass
        return res

    def simulation_over(self, seed):
        res = self._player.simulation_over(seed)
        return res

    def abort(self):
        self._abort = True

        self._player.abort()

        if self._view:
            self._view.set_simulation_complete()
            pass
        
        return

    def unregister_player(self):
        if self._remote_server is not None:
            try:
                self._remote_server.unregister_player(self._player_name)
            except socket.error, ex:
                pass
            pass
        self._remote_server = None
        return

    def run(self):
        self._xmlrpc_server.timeout = 0.1

        while not self._abort:
            if self._xmlrpc_server:
                self._xmlrpc_server.handle_request()
                pass
            pass
        try:
            self._xmlrpc_server.server_close()
        except socket.error, ex:
            if ex[0] == 61:
                pass
            else:
                raise ex
            pass
        return
    
    pass # End of class SortoXMLRPCPlayerThread

